#include "idcard.h"
#include <tmz.h>
#include <unistd.h>
#include <string.h>
#include <iostream>
#include <string>

using namespace std;

namespace idcard
{
enum DataField {
    ALL_ = 0,
    NAME_ = 1,
    GENDER_,
    ETHNICITY_,
    BIRTH_YEAR_,
    BIRTH_MONTH_,
    BIRTH_DAY_,
    ADDRESS_,
    ID_NUMBER_,
    DEPARTMENTS_,
    START_YEAR_,
    START_MONTH_,
    START_DAY_,
    EXPIRY_YEAR_,
    EXPIRY_MONTH_,
    EXPIRY_DAY_,
    SN_,
    DATA_FIELD_COUNT
};

template <int type>
struct FieldClass {
    FieldClass() : type_(type) {}
    operator int() const noexcept {return type_;}
private:
    int type_;
};

#define generate_field(a) static FieldClass<a> a##field

generate_field(NAME_);
generate_field(GENDER_);
generate_field(ETHNICITY_);
generate_field(BIRTH_YEAR_);
generate_field(BIRTH_MONTH_);
generate_field(BIRTH_DAY_);
generate_field(ADDRESS_);
generate_field(ID_NUMBER_);
generate_field(DEPARTMENTS_);
generate_field(START_YEAR_);
generate_field(START_MONTH_);
generate_field(START_DAY_);
generate_field(EXPIRY_YEAR_);
generate_field(EXPIRY_MONTH_);
generate_field(EXPIRY_DAY_);
generate_field(SN_);

const int FieldIndex[DATA_FIELD_COUNT] = {
    [ALL_] = 999,
    [NAME_] = 0,
    [GENDER_] = 1,
    [ETHNICITY_] = 2,
    [BIRTH_YEAR_] = 3,
    [BIRTH_MONTH_] = 3,
    [BIRTH_DAY_] = 3,
    [ADDRESS_] = 4,
    [ID_NUMBER_] = 5,
    [DEPARTMENTS_] = 6,
    [START_YEAR_] = 7,
    [START_MONTH_] = 7,
    [START_DAY_] = 7,
    [EXPIRY_YEAR_] = 8,
    [EXPIRY_MONTH_] = 8,
    [EXPIRY_DAY_] = 8,
    [SN_] = 999
};

enum PictureType {
    PIC_ICON = 1,
    PIC_FRONT,
    PIC_BACK,
    PIC_FRONT_BACK_V,
    PIC_BACK_FRONT_V,
    PIC_FRONT_BACK_H,
    PIC_BACK_FRONT_H
};

enum CredentialsType {
    IDCAR,
    RESIDE,
    HK_MAC_TW /* Hongkong, Macao, Taiwan */
};

typedef struct Device_
{
    Device_() : fd_(0) {  }
    operator long() const noexcept {return fd_;}
    void operator=(long fd) {fd_ = fd;}
    long getFd() const { return fd_; }
    void setFd(long &fd) { fd_ = fd; }
private:
    long fd_;
} Device;

static Device idevice;

int init()
{
    int n;
    long dev_fd = ICReaderOpenUsbByFD(0);
    if (dev_fd <= 0) {
        cerr << "Init error: ICReader can not open USB device!" << endl;
        return -1;
    }

    idevice = dev_fd;

    auto non_zero_warnning = [&n](const string &fail_desc, const string &succ_desc) {
        cout << (0 != n ? fail_desc : succ_desc) << endl;
    };

    char SN[24] = {0};  /* serial number */
    n = ICReaderReadDevSnr(dev_fd, 20, SN);
    non_zero_warnning("ICReader[init]: failed to read serial number!", string("ICReader serial number: ") + SN);

    char version[64] = {0};
    n = ICReaderGetVer(dev_fd, version);
    non_zero_warnning("ICReader[init]: failed to read version number!", string("ICReader version: ") + version);

    n = ICReaderBeep(dev_fd, 1, 1, 2);
    non_zero_warnning("ICReader[init]: failed to beep!", "ICReader beep...");

    return 0;
}

int close()
{
    int n = ICReaderClose(idevice);
    if (0 != n)
        cout << "ICReader[close]: Failed to Close device!" << endl;

    return n;
}

/* status reference
    switch (stat)
    {
    case 0x00:
        RESULT_PRINTF("设备正常,状态值:[%d]\r\n",devstatus);
        break;
    case 0x01:
        RESULT_PRINTF("接触卡通道异常,状态值:[%d]\r\n",devstatus);
        break;
    case 0x02:
        RESULT_PRINTF("非接卡通道异常,状态值:[%d]\r\n",devstatus);
        break;
    case 0x03:
        RESULT_PRINTF("接触卡和非接卡通道异常,状态值:[%d]\r\n",devstatus);
        break;
    case 0x04:
        RESULT_PRINTF("安全模块通道异常,状态值:[%d]\r\n",devstatus);
        break;
    case 0x05:
        RESULT_PRINTF("接触卡和安全模块通道异常,状态值:[%d]\r\n",devstatus);
        break;
    case 0x06:
        RESULT_PRINTF("非接卡和安全模块通道异常,状态值:[%d]\r\n",devstatus);
        break;
    case 0x07:
        RESULT_PRINTF("接触卡、非接卡和安全模块通道异常,状态值:[%d]\r\n",devstatus);
        break;
    default:
        RESULT_PRINTF("其他状态.[%d]\r\n.",devstatus);
        break;
    }
*/
int getStatus()
{
    unsigned char stat = 0;
    int n = ICReaderDevStatus(idevice, &stat);

    if (0 != n) {
        cout << "ICReader[getStatus]: Failed to read device status!" << endl;
        stat = 2; /* any non-zero digit */
    }

    if (0x00 != stat)
        n = 2;

    return n;
}

/*
idVendor=5929, idProduct=68a6, bcdDevice= 2.00
strings: Mfr=1, Product=2, SerialNumber=3
Product: TMZ_ZSHs
Manufacturer: Skyper
SerialNumber: sk0000000000

{
"type":"1070",
"name":"ICReader",
"manufacturers":"xxx",
"model":"xxx",
"sdk":"xxx",
"description":"xxx"
}
*/
const char *getInfo()
{
    static string info;
    if (info.empty()) {
        info += "{\n\"type\":\"1070\",\n";
        info += "\"name\":\"ICReader\",\n";
        info += "\"manufacturers\":\"Skyper\",\n";
        info += "\"model\":\"Skyper TMZ_ZSHs\",\n";
        info += "\"sdk\":\"1.0.0\",\n";
        info += "\"description\":\"IC card reader\"\n}";
    }

    return info.c_str();
}

template<int type>
static int getImage(char **out, int &len)
{
    return -1;
}

template<>
int getImage<PIC_ICON>(char **out, int &len)
{
    unsigned char ch_buff[264] = { 0 };  /* character hex message */
    unsigned char ph_buff[1032] = { 0 }; /* photo hex message */
    unsigned char fp_buff[1032] = { 0 }; /* fingerprint hex message */
    int ch_len = 256;
    int ph_len = 1024;
    int fp_len = 1024;
    int fp_flag  = 0;
    int n = 0;

    n = ReadIDCardBaseMsg(idevice, fp_flag, ch_buff, &ch_len, ph_buff, &ph_len, fp_buff, &fp_len);
    if (0 != n) {
        cerr << "ICReader[getImage]: Read idcard error!" << endl;
        return -1;
    }

#define B64LEN 65536
    *out = new char[B64LEN];
    memset(*out, 0, B64LEN);

    n = ParseIDCardPic(ph_buff, ph_len, 1, *out);
    if (0 != n) {
        cerr << "ICReader[getImage]: Parse picture error!" << endl;
        return -1;
    }
    len = strlen(*out);

    return 0;
}

int getIDCardImages(int type, char **out, int &len)
{
    int n = 0;

    switch (type) {
    case PIC_ICON:
        n = getImage<PIC_ICON>(out, len);
        break;
    case PIC_FRONT:
        break;
    case PIC_BACK:
        break;
    case PIC_FRONT_BACK_V:
        break;
    case PIC_BACK_FRONT_V:
        break;
    case PIC_FRONT_BACK_H:
        break;
    case PIC_BACK_FRONT_H:
        break;
    }

    return n;
}

template<int field>
static int getData(char *out, int len)
{
    return -1;
}

template<>
int getData<ALL_>(char *out, int len)
{
    unsigned char ch_buff[264] = { 0 };  /* character hex message */
    unsigned char ph_buff[1032] = { 0 }; /* photo hex message */
    unsigned char fp_buff[1032] = { 0 }; /* fingerprint hex message */
    char ch_text[512] = { 0 }; /* base message */
    int ch_len = 256;
    int ph_len = 1024;
    int fp_len = 1024;
    int fp_flag  = 0;
    int n = 0;
    int type = 0;

    n = ReadIDCardBaseMsg(idevice, fp_flag, ch_buff, &ch_len, ph_buff, &ph_len, fp_buff, &fp_len);
    if (0 != n) {
        cerr << "ICReader[getData<ALL_>]: Read idcard error!" << endl;
        return -1;
    }

    ParseIDCardInfo(ch_buff, ch_len, &type, ch_text);

    n = strlen(ch_text);

    if (n > len) {
        cerr << "ICReader[getData<ALL_>]: Require larger memory!" << endl;
        return -1;
    }

    strncpy(out, ch_text, n);

    if (n < len)
        out[n] = '\0';

    return 0;
}

template<>
int getData<SN_>(char *out, int len)
{
    unsigned char buff[64] = { 0 };
    char asc_buff[128] = { 0 };
    int len_ = 0;
    int n = 0;

    n = GetIDCardUID(idevice, (unsigned char *)&len_, buff);
    Hex2Asc(buff, asc_buff, len_);
    len *= 2;

    if (0 == n) {
        int len_ = strlen(asc_buff);
        if (len_ > len) {
            cerr << "ICReader[getData<SN_>]: Require larger memory!" << endl;
            return -1;
        }

        strncpy(out, asc_buff, len_);
        if (len_ < len)
            out[len_] = '\0';
    } else {
        cerr << "ICReader[getData]: Failed to read serial number!" << endl;
    }

    return n;
}

template<int field>
static int getDataInJson(char *out, int len)
{
    return -1;
}

template<>
int getDataInJson<ALL_>(char *out, int len)
{
    /* IDCARD original field */
    enum {
        ID_NAME_ = 0, ID_GENDER_, ID_ETHNICITY_, ID_BIRTH_, ID_ADDRESS_, ID_ID_NUMBER_,
        ID_DEPARTMENTS_, ID_START_, ID_EXPIRY_, ID_RESERVE_
    };
    
    static const char *IDCard_field_s[ID_RESERVE_ + 1] = {
        [ID_NAME_] = "姓名",
        [ID_GENDER_] = "性别",
        [ID_ETHNICITY_] = "民族",
        [ID_BIRTH_] = "出生日期",
        [ID_ADDRESS_] = "住址",
        [ID_ID_NUMBER_] = "公民身份号码",
        [ID_DEPARTMENTS_] = "签发机关",
        [ID_START_] = "起始日期",
        [ID_EXPIRY_] = "截止日期",
        [ID_RESERVE_] = "预留"
    };

#define UNKNOWN_FIELD "未知字段"

    char text[512];
    int n = getData<ALL_>(text, 512);

    if (n < 0)
        return -1;

    int begin = 0;
    int idx = 0;

    string str("{");
    for (size_t i = 0; i < strlen(text); ++i) {
        if (text[i] == '|') {
            str += "\"";
            str += idx > ID_RESERVE_ ? UNKNOWN_FIELD : IDCard_field_s[idx];
            str += string("\":\"") + string(text, begin, i - begin) + "\",";
            ++idx;
            begin = i + 1;
        }
    }

    int len_ = str.size();
    str[len_ - 1] = '}';

    if (len_ > len) {
        cerr << "ICReader[getDataInJson<ALL_>]: Require large memory!" << endl;
        return -1;
    }

    strncpy(out, str.c_str(), len_);
    if (len_ < len)
        out[len_] = '\0';

    return 0;
}

template<typename Field>
static int getData(const Field &field, char *out, int len)
{
    char text[512];
    int n = getData<ALL_>(text, 512);

    if (0 != n)
        return n;

    int begin = -1;
    int count = 0;
    int sep_c = 0; /* separator count */
    const int idx = FieldIndex[field];

    for (size_t i = 0; i < strlen(text); ++i) {
        if (begin < 0 && sep_c == idx)
            begin = i;
        if (text[i] == '|')
            ++sep_c;
        if (sep_c == idx + 1) {
            count = i - begin;
            break;
        }
    }

    if (count > len) {
        cerr << "ICReader[getData<ALL_>]: Require large memory!" << endl;
        return -1;
    }

    char *sub = text + begin;
    strncpy(out, sub, count);
    if (count < len)
        out[count] = '\0';

    return 0;
}

template<typename Field>
static int getYear(const Field &field, char *out, int len)
{
    char text[512];
    int n = getData<ALL_>(text, 512);

    if (0 != n)
        return n;

    int begin = -1;
    int count = 0;
    int sep_c = 0; /* separator count */
    const int idx = FieldIndex[field];

    for (size_t i = 0; i < strlen(text); ++i) {
        if (begin < 0 && sep_c == idx)
            begin = i;
        if (text[i] == '|')
            ++sep_c;
        if (sep_c == idx + 1) {
            count = i - begin;
            break;
        }
    }

    char *sub = text + begin;
    count = 4;

    if (count > len) {
        cerr << "ICReader[getYear]: Require large memory!" << endl;
        return -1;
    }

    strncpy(out, sub, count);
    if (count < len)
        out[count] = '\0';

    return 0;
}

template<>
int getData<FieldClass<BIRTH_YEAR_> >(const FieldClass<BIRTH_YEAR_> &field, char *out, int len)
{
    return getYear(field, out, len);
}

template<>
int getData<FieldClass<START_YEAR_> >(const FieldClass<START_YEAR_> &field, char *out, int len)
{
    return getYear(field, out, len);
}

template<>
int getData<FieldClass<EXPIRY_YEAR_> >(const FieldClass<EXPIRY_YEAR_> &field, char *out, int len)
{
    return getYear(field, out, len);
}

template<typename Field>
static int getMonth(const Field &field, char *out, int len)
{
    char text[512];
    int n = getData<ALL_>(text, 512);

    if (0 != n)
        return n;

    int begin = -1;
    int count = 0;
    int sep_c = 0; /* separator count */
    const int idx = FieldIndex[field];

    for (size_t i = 0; i < strlen(text); ++i) {
        if (begin < 0 && sep_c == idx)
            begin = i;
        if (text[i] == '|')
            ++sep_c;
        if (sep_c == idx + 1) {
            count = i - begin;
            break;
        }
    }

    char *sub = text + begin;
    sub += 4;
    count = 2;

    if (count > len) {
        cerr << "ICReader[getMonth]: Require large memory!" << endl;
        return -1;
    }

    strncpy(out, sub, count);
    if (count < len)
        out[count] = '\0';

    return 0;
}

template<>
int getData<FieldClass<BIRTH_MONTH_> >(const FieldClass<BIRTH_MONTH_> &field, char *out, int len)
{
    return getMonth(field, out, len);
}

template<>
int getData<FieldClass<START_MONTH_> >(const FieldClass<START_MONTH_> &field, char *out, int len)
{
    return getMonth(field, out, len);
}

template<>
int getData<FieldClass<EXPIRY_MONTH_> >(const FieldClass<EXPIRY_MONTH_> &field, char *out, int len)
{
    return getMonth(field, out, len);
}

template<typename Field>
static int getDay(const Field &field, char *out, int len)
{
    char text[512];
    int n = getData<ALL_>(text, 512);

    if (0 != n)
        return n;

    int begin = -1;
    int count = 0;
    int sep_c = 0; /* separator count */
    const int idx = FieldIndex[field];

    for (size_t i = 0; i < strlen(text); ++i) {
        if (begin < 0 && sep_c == idx)
            begin = i;
        if (text[i] == '|')
            ++sep_c;
        if (sep_c == idx + 1) {
            count = i - begin;
            break;
        }
    }

    char *sub = text + begin;
    sub += 6;
    count = 2;

    if (count > len) {
        cerr << "ICReader[getDay]: Require larger memory!" << endl;
        return -1;
    }

    strncpy(out, sub, count);
    if (count > len)
    out[count] = '\0';

    return 0;
}

template<>
int getData<FieldClass<BIRTH_DAY_> >(const FieldClass<BIRTH_DAY_> &field, char *out, int len)
{
    return getDay(field, out, len);
}

template<>
int getData<FieldClass<START_DAY_> >(const FieldClass<START_DAY_> &field, char *out, int len)
{
    return getDay(field, out, len);
}

template<>
int getData<FieldClass<EXPIRY_DAY_> >(const FieldClass<EXPIRY_DAY_> &field, char *out, int len)
{
    return getDay(field, out, len);
}

int getIDCardData(int field, char *out, int len)
{
    if (field < ALL_ || field >= DATA_FIELD_COUNT) {
        cout << "ICReader[getIDCardData]: Invalid parameter" << endl;
        return -1;
    }

    int ret;

#define concat_field(a) a ## field

    switch (field)
    {
    case NAME_:
        ret = getData(concat_field(NAME_), out, len);
        break;
    case GENDER_:
        ret = getData(concat_field(GENDER_), out, len);
        break;
    case ETHNICITY_:
        ret = getData(concat_field(ETHNICITY_), out, len);
        break;
    case BIRTH_YEAR_:
        ret = getData(concat_field(BIRTH_YEAR_), out, len);
        break;
    case BIRTH_MONTH_:
        ret = getData(concat_field(BIRTH_MONTH_), out, len);
        break;
    case BIRTH_DAY_:
        ret = getData(concat_field(BIRTH_DAY_), out, len);
        break;
    case ADDRESS_:
        ret = getData(concat_field(ADDRESS_), out, len);
        break;
    case ID_NUMBER_:
        ret = getData(concat_field(ID_NUMBER_), out, len);
        break;
    case DEPARTMENTS_:
        ret = getData(concat_field(DEPARTMENTS_), out, len);
        break;
    case START_YEAR_:
        ret = getData(concat_field(START_YEAR_), out, len);
        break;
    case START_MONTH_:
        ret = getData(concat_field(START_MONTH_), out, len);
        break;
    case START_DAY_:
        ret = getData(concat_field(START_DAY_), out, len);
        break;
    case EXPIRY_YEAR_:
        ret = getData(concat_field(EXPIRY_YEAR_), out, len);
        break;
    case EXPIRY_MONTH_:
        ret = getData(concat_field(EXPIRY_MONTH_), out, len);
        break;
    case EXPIRY_DAY_:
        ret = getData(concat_field(EXPIRY_DAY_), out, len);
        break;
    case SN_:
        ret = getData<SN_>(out, len);
        break;
    case ALL_:
    default:
        ret = getDataInJson<ALL_>(out, len);
        break;
    }

    return ret;
}

} // namespace idcard
